// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace Microsoft.AspNetCore.Authentication.AzureADB2C.UI;

[Obsolete("This is obsolete and will be removed in a future version. Use Microsoft.Identity.Web instead. See https://aka.ms/ms-identity-web.")]
internal sealed class AzureADB2COpenIDConnectEventHandlers
{
    private readonly IDictionary<string, string> _policyToIssuerAddress =
        new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public AzureADB2COpenIDConnectEventHandlers(string schemeName, AzureADB2COptions options)
    {
        SchemeName = schemeName;
        Options = options;
    }

    public string SchemeName { get; }

    public AzureADB2COptions Options { get; }

    public Task OnRedirectToIdentityProvider(RedirectContext context)
    {
        var defaultPolicy = Options.DefaultPolicy;
        if (context.Properties.Items.TryGetValue(AzureADB2CDefaults.PolicyKey, out var policy) &&
            !string.IsNullOrEmpty(policy) &&
            !string.Equals(policy, defaultPolicy, StringComparison.OrdinalIgnoreCase))
        {
            context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
            context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
            context.ProtocolMessage.IssuerAddress = BuildIssuerAddress(context, defaultPolicy, policy);
            context.Properties.Items.Remove(AzureADB2CDefaults.PolicyKey);
        }

        return Task.CompletedTask;
    }

    private string BuildIssuerAddress(RedirectContext context, string defaultPolicy, string policy)
    {
        if (!_policyToIssuerAddress.TryGetValue(policy, out _))
        {
            _policyToIssuerAddress[policy] = context.ProtocolMessage.IssuerAddress.ToLowerInvariant()
                .Replace($"/{defaultPolicy.ToLowerInvariant()}/", $"/{policy.ToLowerInvariant()}/");
        }

        return _policyToIssuerAddress[policy];
    }

    public Task OnRemoteFailure(RemoteFailureContext context)
    {
        context.HandleResponse();
        // Handle the error code that Azure Active Directory B2C throws when trying to reset a password from the login page
        // because password reset is not supported by a "sign-up or sign-in policy".
        // Below is a sample error message:
        // 'access_denied', error_description: 'AADB2C90118: The user has forgotten their password.
        // Correlation ID: f99deff4-f43b-43cc-b4e7-36141dbaf0a0
        // Timestamp: 2018-03-05 02:49:35Z
        //', error_uri: 'error_uri is null'.
        if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("AADB2C90118"))
        {
            // If the user clicked the reset password link, redirect to the reset password route
            context.Response.Redirect($"{context.Request.PathBase}/AzureADB2C/Account/ResetPassword/{SchemeName}");
        }
        // Access denied errors happen when a user cancels an action on the Azure Active Directory B2C UI. We just redirect back to
        // the main page in that case.
        // Message contains error: 'access_denied', error_description: 'AADB2C90091: The user has cancelled entering self-asserted information.
        // Correlation ID: d01c8878-0732-4eb2-beb8-da82a57432e0
        // Timestamp: 2018-03-05 02:56:49Z
        // ', error_uri: 'error_uri is null'.
        else if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("access_denied"))
        {
            context.Response.Redirect($"{context.Request.PathBase}/");
        }
        else
        {
            context.Response.Redirect($"{context.Request.PathBase}/AzureADB2C/Account/Error");
        }

        return Task.CompletedTask;
    }
}
